#ifndef __ENGINE_H__
#define __ENGINE_H__

#include "engine_global.h"

#include <QDebug>
#include <QImage>
#include <QTime>
#include <QTimer>
#include <QElapsedTimer>
#include <QGraphicsView>
#include <QGraphicsItem>
#include <QGraphicsScene>
#include <QVariantAnimation>
#include <QEvent>

class GLoop;
class GRenderer;
class GAnimate;
class GObject;
class GGame;
class GWindow;

typedef std::list<GObject*>             GObjects;
typedef std::list<GObject*>::iterator	GObjects_itor;
typedef std::list<GAnimate*>            GAnimates;
typedef std::list<GAnimate*>::iterator	GAnimates_itor;

extern inline QPointF	bi_lerp(const QPointF& v0, const QPointF& v1, const double t);
extern inline QPointF	bi_bspline(const QPointF& v0, const QPointF& v1, const QPointF& v2, const QPointF& v3, const double t);

////////////////////////////////////////////////
// GameEngine
////////////////////////////////////////////////
class G_EXPORT GEngine
{
public:
    GEngine();
    virtual ~GEngine();

    // singleton
    static GGame*       GetGame();
    static GEngine*     GetEngine();
    static GRenderer*   GetRenderer();

public:
    bool    EngineInitialize(GGame* pGame);
    void    EngineStart();
    void    EngineEnd();

    void    Tick(double elapsed);


protected:

private:
    static GEngine		*m_pEngine;
    static GGame		*m_pGame;
    static GRenderer	*m_pRenderer;
    
	GLoop				*m_pLoop;
};
extern GEngine g_engine;

////////////////////////////////////////////////
// Game Loop
////////////////////////////////////////////////
class G_EXPORT GLoop : public QTimer
{
    Q_OBJECT

public:
    GLoop();
    ~GLoop();
    
	void    StartLoop();
    void    StopLoop();

protected slots:
    void	Loop();

private:
    QElapsedTimer   m_elapsedTimer;
};


////////////////////////////////////////////////
// Game Manager
////////////////////////////////////////////////
class G_EXPORT GAnimate
{
public:
    GAnimate(QEvent::Type type, QGraphicsItem* pItem, QVariant end, double duration);	
    ~GAnimate();

	QGraphicsItem*	GetItem();
    QEvent::Type	GetType();
    QTime&			GetStartTime();
    double			GetDuration();
	    
    void			SetStart();
    void			DoAnimate(double elapsed);
    void			Finish();
	
protected:
	QEvent::Type	m_type;
    QGraphicsItem	*m_pItem;
    QVariant		m_start;
    QVariant		m_end;
    QTime			m_startTime;
    double			m_duration;
};

////////////////////////////////////////////////
// Game Manager
////////////////////////////////////////////////
typedef std::list<GAnimate*>              GAnimates;
typedef std::list<GAnimate*>::iterator    GAnimates_itor;
class G_EXPORT GGame : public QObject
{
	Q_OBJECT

public:
    GGame();
    virtual ~GGame();

	void		SetGameWindow(GWindow* pWindow);   
    GWindow*    GetWindow();
    GObjects*   GetAllObjects();
    GAnimates*  GetAnimateObjects();

public:
    virtual bool    GameInitialize();
    virtual void    GameStart();
    virtual void    GameEnd();
    virtual void    Update(double elapsed) = 0;

    void            AddObject(GObject* pObject, int x=0, int y=0);
    void            AddAnimate(GAnimate* pAni);
   	GObject*        FindObject(QString name);
	void			GarbageCollect();

protected:
	
    virtual bool    event(QEvent* event);

protected:
    GObjects    m_go_all;
    GAnimates	m_go_animate;
	
    GWindow     *m_pWindow;
};

////////////////////////////////////////////////
// Game Renderer
////////////////////////////////////////////////
class G_EXPORT GRenderer
{
public:
    GRenderer();
    virtual ~GRenderer();

public:
    virtual bool    RendererInitialize(GWindow* pWnd);
    virtual void    RendererStart();
    virtual void    RendererEnd();

    void    Render(double elapsed);

private:
    GWindow       *m_pWindow;
};

////////////////////////////////////////////////
// Game Window
////////////////////////////////////////////////
class G_EXPORT GWindow : public QGraphicsView
{
public:
    GWindow(QWidget *parent = 0);
    virtual ~GWindow();

	QGraphicsScene* GetScene();

public:
    void    AddObject(GObject* pObject);
	void	AddItem(QGraphicsItem* pItem, int x, int y);

protected:
    QGraphicsScene  m_scene;
};

////////////////////////////////////////////////
// Game Object
////////////////////////////////////////////////
class G_EXPORT GObject : public QGraphicsObject //public QGraphicsItem
{
    Q_OBJECT

public:
    GObject(QString& name, QGraphicsItem *parent = 0);
    ~GObject();

	bool			GetGarbage();
    const QString&  GetName();

public:
    void	Destroy();
	void	StartAnimation(QEvent::Type type, QVariant value, double duration, QGraphicsItem* pItem = 0);
    void    StopAnimation();

protected:
    virtual bool    event(QEvent* event);
    virtual void    FinishedAnimation(QEvent::Type type, void* param);
	virtual void    CollisionDetected(void* param);
	
protected:
    bool        m_bGarbage;
    QString     m_name;
};

////////////////////////////////////////////////
// GEvent
////////////////////////////////////////////////
class G_EXPORT GEffect : public GObject 
{
    Q_OBJECT

public:
    GEffect(QString name, QGraphicsItem *parent = 0);
    ~GEffect();
	
	void	SetSingleShot(bool shot);
	void	SetImage(QString path, int frame_w, int frame_h);
	int		GetTotalFrame();
	int		GetCurrentFrame();

public:
	void	SetFrame(int frame);
	void	NextFrame();

protected:
    virtual QRectF  boundingRect() const;
    virtual void    paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
	virtual void	FinishedAnimation(QEvent::Type type, void* param);
	
protected:
	bool	m_bSingleShot;
	QImage	m_image, *m_pFrame;
	int		m_nFrameWidth, m_nFrameHeight;
	int		m_current_frame, m_total_frame;
};

////////////////////////////////////////////////
// GEvent
////////////////////////////////////////////////
class G_EXPORT GEvent : public QEvent
{
public:
	GEvent(QEvent::Type type, void* param = 0);
    ~GEvent();

	static const QEvent::Type	Evt_MoveTo;
    static const QEvent::Type	Evt_RotateTo;
    static const QEvent::Type	Evt_ScaleTo;
    static const QEvent::Type	Evt_AlphaTo;
	static const QEvent::Type	Evt_Effect;
	static const QEvent::Type	Evt_Collision;
	static const QEvent::Type	Evt_Garbage;

	void*	param();
private:
	void*	m_param;
};

#endif // __ENGINE_H__
